#include "task.hpp"
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <cstdlib>
#include <cassert>
#include <ctime>   

const int processnum=10;             //10                   进程
std::vector<task_t> tasks;          //存放任务

//先描述，一个通道的属性 
class channel
{
public:
    channel(int cmdfd,int slaverid,const std::string &processname)
    :_cmdfd(cmdfd),_slaverid(slaverid),_processname(processname)
    {}
public:
    int _cmdfd;                      //发送任务的文件描述符
    pid_t _slaverid;                 //子进程pid
    std::string _processname;        //子进程名字 --- 方便打印日志
};

//读取   
void slaver()
{
    //read(0);                           //这样子进程直接从0号标准输入文件读转换为pipefd[0]读

    while(true)
    {
        int cmdcode = 0;                //任务码编号
        int n = read(0,&cmdcode,sizeof(int));   //向0号文件描述符读取任务为固定大小字节
        if(n==sizeof(int))              //控制向管道传入固定大小字节
        {
            //执行cmdcode对应的任务列表
            std ::cout<<"slaver say@ get a command:"<<getpid()<<":cmdcode "<<cmdcode<<std::endl;
            if(cmdcode>=0&&cmdcode<tasks.size()) tasks[cmdcode]();
        }
        if(n==0)  break;      
    }
}

void Menu()
{
    std::cout<<"******************************"<<std::endl;
    std::cout<<"****1.起床阶段   2.学习阶段****"<<std::endl;
    std::cout<<"****3.吃饭阶段   4.睡前阶段****"<<std::endl;
    std::cout<<"****0.退出阶段             ****"<<std::endl;
    std::cout<<"******************************"<<std::endl;
}
void ctrlSlaver(const std::vector<channel> &channels)
{
    int which=0;   
    while(true)                     
    {
        
        int select=0;
        Menu();
        std::cout<<"please Enter@ ";
        std::cin>>select;
        if(select <=0||select>=5)  break;
        

        //1.选择任务
        //int cmdcode = rand()%tasks.size();                //随机分发任务
        int cmdcode=select-1;
        
        //2.选择子进程
        //int processpos = rand()%channels.size();          //随机分发给子进程

        std::cout<<"father say:"<<"cmdcode: "<<cmdcode<<" already sendto "<<
        channels[which]._slaverid<<" process name"<<channels[which]._processname <<std::endl;
 
        //3.发送任务
        write(channels[which]._cmdfd,&cmdcode,sizeof(cmdcode));   
   
        which++;
        which%=channels.size(); 
        sleep(1);
    }
}

void InitProcessPool(std::vector<channel> *channels)
{
     //初始化
     for(int i=0;i<processnum;i++)      //创建5个子进程
     {
         int pipefd[2];                 //文件描述符
         int n = pipe(pipefd);          //创建管道
         assert(!n);                    //创建成功返回0
         (void)n;
 
         pid_t id=fork();

         //子进程
         if(id==0)                       //子进程读
         {
             close(pipefd[1]);
             dup2(pipefd[0],0);          //为了把从管道文件的读替换为从0号文件标准输入文件读
             close(pipefd[0]);
             slaver();                   //调用函数，这样就不用传参了
             std::cout<<"process :" <<getpid()<<" quit"<<std::endl;
             exit(0);                    //只执行子进程的代码
         }

         //父进程
         close(pipefd[0]);               //父进程写
         
         //向channel对象写入数据
         std::string name="process-" + std::to_string(i);       //把循环语句的下标转为字符串

         //写入管道对应参数：写入位置，进程id，该进程的下标
         channels->push_back(channel(pipefd[1],id,name));       
     }
}

void Debug(const std::vector<channel> &channels)
{
    for(const auto &e :channels)
    {
        std::cout<< e._cmdfd<<" "<<e._slaverid<<" "<<e._processname<<std::endl;
    }
}

void quitProcess(const std::vector<channel> &channels)
{
    int last=channels.size()-1;
    for(int i=last;i>=0;i--)
    {
        close(channels[i]._cmdfd);
        waitpid(channels[i]._slaverid,nullptr,0);
    }

}

//再组织
int main()
{
    LoadTask(&tasks);                           //装载任务
    srand(time(nullptr)^getpid()^1023);         //随机数种子，每次都不一样且数字间隔较为离散 

    //把描述属性的集合转为对象的管理也就是文件描述符对应的管道
    std::vector<channel> channels;

    //1.初始化 
    InitProcessPool(&channels);

    //test
    //Debug(channels);

    //2.父进程给子进程分发任务
    ctrlSlaver(channels);
    

    //3.清理收尾
    //quitProcess(channels);

    return 0;
}
